/*
*Copyright 2021-2023 NERCIS
*
*Licensed under the Apache License, Version 2.0 (the "License");
*you may not use this file except in compliance with the License.
*You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*Unless required by applicable law or agreed to in writing, software
*distributed under the License is distributed on an "AS IS" BASIS,
*WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*See the License for the specific language governing permissions and
*limitations under the License.
*/

package cn.ac.nercis.pes.community.service.photo;

import cn.ac.nercis.pes.common.constant.common.BaseConstants;
import cn.ac.nercis.pes.common.constant.knowledge.LayerTarget;
import cn.ac.nercis.pes.model.dto.common.Result;
import cn.ac.nercis.pes.repository.dal.evaluation.EvaluationRecordRepository;
import cn.ac.nercis.pes.repository.model.evaluation.EvaluationRecordDO;
import cn.ac.nercis.pes.service.utils.FileManage;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 证据照片文件服务类
 * @author zpy
 * @version 1.0.0
 */
@Slf4j
@Service
public class PhotoFileService {

    private final EvaluationRecordRepository evaluationRecordRepository;

    @Autowired
    public PhotoFileService(EvaluationRecordRepository evaluationRecordRepository) {
        this.evaluationRecordRepository = evaluationRecordRepository;
    }

    public List<String> findPhotoFileNames(@NonNull String systemId){
        return Stream.of(LayerTarget.values())
                .flatMap(layerTarget->findPhotoFileNames(systemId,layerTarget).stream())
                .filter(StringUtils::isNotEmpty)
                .collect(Collectors.toList());
    }

    public List<File> findPhotoFiles(@NonNull String systemId,@NonNull String query){
        return Stream.of(LayerTarget.values())
                .flatMap(layerTarget->findPhotoFiles(systemId,layerTarget).stream())
                .filter(f->StringUtils.contains(f.getName(),query))
                .collect(Collectors.toList());
    }

    public List<File> findPhotoFiles(@NonNull String systemId,@NonNull LayerTarget layerTarget){

        File dirFile = FileUtils.getFile(FileManage.getPhotoFileDir(systemId,layerTarget.getName()));
        if(dirFile.exists() && dirFile.isFile()){
            FileManage.deleteFile(dirFile);
        }
        if(!dirFile.exists()){
            return Collections.emptyList();
        }
        File[] photoFiles = dirFile.listFiles(FileManage.getPhotoFileFilter());
        if(Objects.isNull(photoFiles) || photoFiles.length == 0){
            return Collections.emptyList();
        }
        return Arrays.asList(photoFiles);
    }

    public List<String> findPhotoFileNames(@NonNull String systemId,@NonNull LayerTarget layerTarget){
        return findPhotoFiles(systemId,layerTarget)
                .stream()
                .map(File::getName)
                .collect(Collectors.toList());
    }

    public Result<String> refreshPhotoFileRename(@NonNull String systemId, @NonNull String oldName, @NonNull String newName){
       List<EvaluationRecordDO> evaluationRecords =  evaluationRecordRepository.findAllBySystemId(systemId);
       if(CollectionUtils.isEmpty(evaluationRecords)){
           return Result.success();
       }
       List<EvaluationRecordDO> updateData = evaluationRecords.stream()
               .filter(er->checkPhotoFileRef(er,oldName))
               .peek(er->updatePhotoFileRef(er,oldName,newName))
               .collect(Collectors.toList());
       try{
           if(CollectionUtils.isNotEmpty(updateData)){
               evaluationRecordRepository.saveAll(updateData);
           }
           return Result.success();
       }catch (Exception ex){
           log.error("向现场测评更新文件变更失败！",ex);
           return Result.failed("向现场测评更新文件变更失败！");
       }
    }

    public Result<String> checkPhotoFileDelete(@NonNull String systemId,@NonNull String fileName){
        List<EvaluationRecordDO> evaluationRecords =  evaluationRecordRepository.findAllBySystemId(systemId);
        if(CollectionUtils.isEmpty(evaluationRecords)){
            return Result.success();
        }
        boolean isRef = evaluationRecords.stream().anyMatch(er->checkPhotoFileRef(er,fileName));
        if(isRef){
            return Result.failed("当前证据文件已被现场测评使用，是否继续进行删除？");
        }else{
            return Result.success();
        }
    }

    public Result<String> deletePhotoFileRef(@NonNull String systemId,@NonNull String fileName){
        List<EvaluationRecordDO> evaluationRecords =  evaluationRecordRepository.findAllBySystemId(systemId);
        if(CollectionUtils.isEmpty(evaluationRecords)){
            return Result.success();
        }
        List<EvaluationRecordDO> updateData = evaluationRecords.stream()
                .filter(er->checkPhotoFileRef(er,fileName))
                .peek(er->deletePhotoFileRef(er,fileName))
                .collect(Collectors.toList());
        try{
            if(CollectionUtils.isNotEmpty(updateData)){
                evaluationRecordRepository.saveAll(updateData);
            }
            return Result.success();
        }catch (Exception ex){
            String errorInfo = String.format("删除现场测评所使用的%s证据文件失败！",fileName);
            log.error(errorInfo,ex);
            return Result.failed(errorInfo);
        }
    }

    private boolean checkPhotoFileRef(EvaluationRecordDO recordDO,String fileName){
        if(checkPhotoFileRef(recordDO.getDFiles(),fileName)){
            return true;
        }
        if(checkPhotoFileRef(recordDO.getAFiles(),fileName)){
            return true;
        }
        if(checkPhotoFileRef(recordDO.getKFiles(),fileName)){
            return true;
        }
        return checkPhotoFileRef(recordDO.getManageFiles(),fileName);
    }

    private boolean checkPhotoFileRef(String fileNames,String fileName){
        if(StringUtils.isEmpty(fileNames)){
            return false;
        }
        String[] photoFileNames = StringUtils.split(fileNames, BaseConstants.MULTI_SPLIT_CHAR);
        return Arrays.stream(photoFileNames).anyMatch(name->StringUtils.equals(name,fileName));
    }

    private void updatePhotoFileRef(EvaluationRecordDO recordDO,String oldFileName,String newFileName){
        if(StringUtils.contains(recordDO.getDFiles(),oldFileName)){
            recordDO.setDFiles(getPhotoFileRef(recordDO.getDFiles(),oldFileName,newFileName));
        }
        if(StringUtils.contains(recordDO.getAFiles(),oldFileName)){
            recordDO.setAFiles(getPhotoFileRef(recordDO.getAFiles(),oldFileName,newFileName));
        }
        if(StringUtils.contains(recordDO.getKFiles(),oldFileName)){
            recordDO.setKFiles(getPhotoFileRef(recordDO.getKFiles(),oldFileName,newFileName));
        }
        if(StringUtils.contains(recordDO.getManageFiles(),oldFileName)){
            recordDO.setManageFiles(getPhotoFileRef(recordDO.getManageFiles(),oldFileName,newFileName));
        }
    }

    private String getPhotoFileRef(String fileNames,String oldFileName,String newFileName){
        String[] photoFileNames = StringUtils.split(fileNames, BaseConstants.MULTI_SPLIT_CHAR);
        return Arrays.stream(photoFileNames)
                .filter(StringUtils::isNotBlank)
                .map(name-> StringUtils.equals(name,oldFileName) ? newFileName : name)
                .collect(Collectors.joining(BaseConstants.MULTI_SPLIT_CHAR));
    }

    private void deletePhotoFileRef(EvaluationRecordDO recordDO,String fileName){
        if(StringUtils.contains(recordDO.getDFiles(),fileName)){
            recordDO.setDFiles(removePhotoFileRef(recordDO.getDFiles(),fileName));
        }
        if(StringUtils.contains(recordDO.getAFiles(),fileName)){
            recordDO.setAFiles(removePhotoFileRef(recordDO.getAFiles(),fileName));
        }
        if(StringUtils.contains(recordDO.getKFiles(),fileName)){
            recordDO.setKFiles(removePhotoFileRef(recordDO.getKFiles(),fileName));
        }
        if(StringUtils.contains(recordDO.getManageFiles(),fileName)){
            recordDO.setManageFiles(removePhotoFileRef(recordDO.getManageFiles(),fileName));
        }
    }

    private String removePhotoFileRef(String fileNames,String fileName){
        String[] photoFileNames = StringUtils.split(fileNames, BaseConstants.MULTI_SPLIT_CHAR);
        return Arrays.stream(photoFileNames)
                .filter(name->StringUtils.isNotBlank(name) && (!StringUtils.equals(name,fileName)))
                .collect(Collectors.joining(BaseConstants.MULTI_SPLIT_CHAR));
    }

}